import cv2
import numpy as np
from collections import Counter
import math
import os
import time
import json
import logging
import traceback
import sys
#带状图片100倍单像素精度 0.617284um/pixel
#专家判定超参数
ump=0.617284
# 设置过滤参数
min_area = 500      # 最小面积
max_area = 1000000    # 最大面积
min_aspect_ratio = 0.000005  # 最小长宽比
max_aspect_ratio = 0.5  # 最大长宽比
minshow=100
fulllenth=800
fovpad=10
stride=50

def analyze_stripes(L, W, L1, W1, fixed_rects, stripe_rects):
    """
    遍历视场中所有800*800区域内的等级
    
    参数:
        L, W: 二值图的总长和总宽
        L1, W1: 固定矩形区域的长和宽
        fixed_rects: 固定矩形区域的左上角坐标列表，格式为[(x1, y1), (x2, y2), ...]
        stripe_rects: 条带区域的外接矩形列表，格式为[(x, y, w, h), ...]（w=宽度，h=高度）
    
    返回:
        (max_count_rect, max_length_rect): 包含最多条带数的矩形位置和包含最长总条带长度的矩形位置
    """
    max_count = 0
    max_count_rect = None
    max_total_length = 0
    max_length_rect = None
    
    # 遍历每个固定矩形区域
    for fx, fy in fixed_rects:
        count = 0
        total_length = 0
        
        # 计算固定矩形区域的边界
        fixed_left = fx
        fixed_right = fx + L1
        fixed_top = fy
        fixed_bottom = fy + W1
        
        # 遍历每个条带区域
        for sx, sy, sw, sh in stripe_rects:
            # 计算条带区域的边界
            stripe_left = sx
            stripe_right = sx + sw
            stripe_top = sy
            stripe_bottom = sy + sh
            
            # 检查条带是否完全包含在固定矩形区域内
            if (stripe_left >= fixed_left and stripe_right <= fixed_right and
                stripe_top >= fixed_top and stripe_bottom <= fixed_bottom):
                
                # 统计条带数量
                count += 1
                # 计算条带长度（取外接矩形长边）
                stripe_length = max(sw, sh)
                total_length += stripe_length
        
        # 更新最多条带数的矩形区域
        if count > max_count:
            max_count = count
            max_count_rect = (fx, fy)
        
        # 更新最长总条带长度的矩形区域
        if total_length > max_total_length:
            max_total_length = total_length
            max_length_rect = (fx, fy)
    
    return max_count_rect, max_length_rect
def rate_banded_structure(params):
    """
    金相带状组织评级函数
    参数params是一个包含以下键的字典：
        # L1_total: 铁素体连续带的总长度（单位：μm）
        # L2_total: 第二相硬质带的总长度（单位：μm）
        n1: 长度≥800μm的铁素体连续带数量
        n2: 0<长度<800μm的铁素体连续带数量
        band_lengths: 所有铁素体带的长度列表（单位：μm）
        n1_band_widths: 所有贯穿带（n1类）的宽度列表（单位：μm），用于4级及以上评级
    """
    # 解包参数
    # L1_total = params['L1_total']
    # L2_total = params['L2_total']
    n1 = params['n1']
    n2 = params['n2']
    band_lengths = params['band_lengths']
    n1_band_widths = params.get('n1_band_widths', [])
    
    # 4级及以上评级（需满足L1_total≥800且L2_total≥800）
    # if L1_total >= 800 and L2_total >= 800:
    #     if not n1_band_widths:
    #         # 无有效宽度数据时使用默认值
    #         min_width, max_width = 1, 1
    #     else:
    #         min_width = min(n1_band_widths)
    #         max_width = max(n1_band_widths)
        
    #     width_ratio = (max_width - min_width) / min_width if min_width > 0 else 0
        
    #     if width_ratio <= 0.2:
    #         return 4
    #     elif width_ratio <= 1.0:
    #         return 4.5
    #     else:
    #         return 5
    if n1>=5 and all(l >= 800 for l in band_lengths):
        return 4
    
    
    # 3.5级：满足两个条件之一
    if (n1 > 4 and n2 >= 1) or (n1 == 4 and n2 >= 1 and any(400 < l < 800 for l in band_lengths)):
        return 3.5
    
    # 3级：满足两个条件之一
    if (n1 == 3 and n2 >= 0) or (n1 == 4 and n2 >= 0 and any(0 < l <= 400 for l in band_lengths)):
        return 3
    
    # 2.5级
    if n1 == 2 and n2 >= 1 and any(400 < l < 800 for l in band_lengths):
        return 2.5
    
    # 2级：满足两个条件之一
    if (n1 == 2 and n2 >= 0 and any(0 < l <= 400 for l in band_lengths)) or \
       (n1 == 1 and n2 >= 1 and any(400 < l < 800 for l in band_lengths)):
        return 2
    
    # 1.5级：满足两个条件之一
    if (n1 == 1 and any(0 < l <= 400 for l in band_lengths)) or \
       (n2 >= 2 and any(400 < l < 800 for l in band_lengths)):
        return 1.5
    
    if len(band_lengths) == 0 :
        return 0
    if all(l < 800 for l in band_lengths):
        return 0.5
    
    
   
    
    # 默认情况（理论上不应到达这里）
    return 0
def filter_contours_by_area_and_aspect_ratio(matimg, min_area=min_area, max_area=max_area, 
                                           min_aspect_ratio=min_aspect_ratio, max_aspect_ratio=max_aspect_ratio):
        """
        根据轮廓的面积和长宽比对二值图进行过滤
        
        参数:
            matimg: 输入的二值图像
            min_area: 最小面积阈值，小于此面积的轮廓将被过滤
            max_area: 最大面积阈值，大于此面积的轮廓将被过滤
            min_aspect_ratio: 最小长宽比阈值
            max_aspect_ratio: 最大长宽比阈值
        
        返回:
            filtered_img: 过滤后的二值图像
            filtered_contours: 过滤后的轮廓列表
        """
    
        # 如果未设置面积阈值，则使用默认值
        if min_area is None:
            min_area = 100  # 默认最小面积
        if max_area is None:
            # 默认最大面积为图像面积的1/10
            max_area = matimg.shape[0] * matimg.shape[1] // 10
        
        # 查找轮廓
        contours, hierarchy = cv2.findContours(matimg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        # 创建空白图像用于绘制过滤后的轮廓
        filtered_img = np.zeros_like(matimg)
        filtered_contours = []
        
        for contour in contours:
            # 计算轮廓面积
            area = cv2.contourArea(contour)
            
            # 计算轮廓的边界矩形
            x, y, w, h = cv2.boundingRect(contour)
            
            # 计算长宽比（避免除零错误）
            if h > 0:
                aspect_ratio = w / h
            else:
                aspect_ratio = float('inf')
            
            # 面积过滤条件
            area_condition = (min_area <= area <= max_area)
            
            # 长宽比过滤条件
            aspect_condition = True
            if min_aspect_ratio is not None and max_aspect_ratio is not None:
                aspect_condition = (min_aspect_ratio <= aspect_ratio <= max_aspect_ratio)
            elif min_aspect_ratio is not None:
                aspect_condition = (aspect_ratio >= min_aspect_ratio)
            elif max_aspect_ratio is not None:
                aspect_condition = (aspect_ratio <= max_aspect_ratio)
            
            # 如果满足所有条件，则保留该轮廓
            if area_condition and aspect_condition:
                filtered_contours.append(contour)
                # 在过滤后的图像上绘制该轮廓（填充）
                cv2.drawContours(filtered_img, [contour], -1, 255, -1)
        
        return filtered_img, filtered_contours
def result_quantization(windowimg):
        result = windowimg
        result = cv2.normalize(result, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)    
        #对result 二值分割图进行cv2.findContours 得到轮廓，并绘制轮廓
        cots=cv2.findContours(result, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        #如过cots 不为空，则获取cots 的最小外接矩形
        count=0
        params=dict()
        band_lengths=[]
        n1_band_widths=[]
        n1=0
        n2=0
        params['n1']=n1
        params['n2']=n2
        params['band_lengths']=band_lengths
        params['n1_band_widths']=n1_band_widths
        if len(cots)>0:
                    
            #循环遍历cots  筛选外接矩形长度大于1000 ，宽度大于1000 ，长宽比大于1的cot，计算数量，并将cot绘制到图像上
            
            for cot in cots[0]:
                x,y,w,h=cv2.boundingRect(cot)
                if  h*ump>=minshow:
                    if  h*ump>=fulllenth and  h/w>1:
                        n1+=1
                        band_lengths.append(h*ump)
                        n1_band_widths.append(w*ump)
                    if  h*ump<fulllenth  and h*ump>minshow:
                        n2+=1
                        band_lengths.append(h*ump)
                        n1_band_widths.append(w*ump)
                        
                        
                    # cv2.rectangle(rcimage,(x,y),(x+w,y+h),(80,30,30),2)
                    # cv2.putText(image,str(f"lenth:{int(h*ump)}um;  width:{int(w*ump)}um"),(x+10,y+20),cv2.FONT_HERSHEY_COMPLEX,1,(0,0,255),1) 
                    # image=put_text_with_background(rcimage,str(f"lenth:{int(h*ump)}um;  width:{int(w*ump)}um"),(x+10,y+30),cv2.FONT_HERSHEY_COMPLEX,1,(255,255,0),(50, 30, 30),thickness=2)    
                    #保存图像
            params['n1']=n1
            params['n2']=n2
            params['band_lengths']=band_lengths
            params['n1_band_widths']=n1_band_widths
            count= rate_banded_structure(params)  
        return count
def analyze_final_level(window_results, method='worst_case'):
    """
    综合分析所有窗口的等级，得到最终等级
    
    参数:
        window_results: 每个窗口的分析结果
        method: 综合方法 ('majority', 'weighted', 'worst_case')
        
    返回:
        final_level: 最终等级
        level_stats: 等级统计信息
    """
    if not window_results:
        return 0, {}
    
    # 提取所有窗口的等级
    levels = [result['level'] for result in window_results]
    
    # 等级统计
    level_counter = Counter(levels)
    total_windows = len(levels)
    
    level_stats = {
        'total_windows': total_windows,
        'level_distribution': dict(level_counter),
        'level_percentage': {level: count/total_windows for level, count in level_counter.items()}
    }
    
    # 根据指定方法确定最终等级
    if method == 'majority':
        # 多数原则：出现最多的等级
        final_level = level_counter.most_common(1)[0][0]
    
    elif method == 'weighted':
        # 加权平均：考虑各等级的比例
        weighted_sum = sum(level * count for level, count in level_counter.items())
        final_level = round(weighted_sum / total_windows)
        # 确保等级在0-3范围内
        final_level = max(0, min(3, final_level))
    
    elif method == 'worst_case':
        # 最差情况：取最高等级
        final_level = max(levels)
    
    else:
        raise ValueError("不支持的统计方法，请选择 'majority', 'weighted' 或 'worst_case'")
    
    return final_level, level_stats

def sliding_window_analysis(filtered_img, window_size=(int(fulllenth/ump)+fovpad, int(fulllenth/ump)+fovpad), stride=stride):
    """
    使用滑动窗口分析二值图像，计算每个窗口的带状组织等级
    
    参数:
        filtered_img: 过滤后的二值图像
        window_size: 滑动窗口大小 (height, width)
        stride: 滑动步长
        
    返回:
        final_level: 最终综合等级
        level_map: 每个窗口的等级分布图
        window_results: 每个窗口的详细结果
    """
    h, w = filtered_img.shape
    window_h, window_w = window_size
    
    # 存储每个窗口的结果
    window_results = []
    
    # 创建等级分布图
    level_map = np.zeros((h, w), dtype=np.uint8)
    
    # 滑动窗口遍历
    for y in range(0, h - window_h-1, stride):
        for x in range(0, w - window_w-1, stride):
            # 提取当前窗口
            window = filtered_img[y:y+window_h, x:x+window_w]
            
            # 计算该窗口的等级
            level = result_quantization(window)
            
            # 记录结果
            window_results.append({
                'position': (x, y),
                'level': level,
                'window_size': (window_w, window_h)
            })
            
            # 在等级分布图中标记该窗口的等级
            level_map[y:y+window_h, x:x+window_w] = level
    
    return window_results, level_map

def overlay_level_map_on_original(original_img, level_map, alpha=0.5):
    """
    将等级图叠加到原图上
    
    参数:
        original_img: 原始图像 (可以是彩色或灰度)
        level_map: 等级图，与original_img相同尺寸
        alpha: 叠加透明度 (0-1之间)
        
    返回:
        overlay_img: 叠加后的图像
    """
    # 确保原图是彩色图像
    if len(original_img.shape) == 2:
        original_img = cv2.cvtColor(original_img, cv2.COLOR_GRAY2BGR)
    
    color_map = {
    0: (0, 255, 0),         # 绿色 - 无带状组织
    0.5: (0, 255, 85),      # 黄绿色 - 极其轻微
    1.0: (0, 255, 170),     # 浅绿色 - 很轻微
    1.5: (0, 255, 255),     # 黄色 - 轻微
    2.0: (0, 170, 255),     # 橙黄色 - 轻度
    2.5: (0, 85, 255),      # 橙色 - 中度
    3.0: (0, 0, 255),       # 红色 - 中重度
    3.5: (42, 0, 213),      # 深红色 - 重度
    4.0: (85, 0, 170),      # 紫红色 - 严重
    4.5: (128, 0, 128),     # 紫色 - 很严重
    5.0: (170, 0, 85),      # 深紫色 - 非常严重
    5.5: (213, 0, 42),      # 暗红色 - 极其严重
    6.0: (255, 0, 0)        # 纯红色 - 极度危险
}
    
    # 创建彩色等级图
    colored_level_map = np.zeros_like(original_img)
    for level, color in color_map.items():
        mask = level_map == level
        colored_level_map[mask] = color
    
    # 将彩色等级图叠加到原图上
    overlay_img = cv2.addWeighted(original_img, 1-alpha, colored_level_map, alpha, 0)
    
    return overlay_img, colored_level_map

def draw_window_boundaries(overlay_img, window_results, color=(255, 255, 255), thickness=1):
    """
    在叠加图像上绘制窗口边界
    
    参数:
        overlay_img: 叠加后的图像
        window_results: 窗口分析结果
        color: 边界线颜色
        thickness: 边界线粗细
    """
    for result in window_results:
        x, y = result['position']
        w, h = result['window_size']
        level = result['level']
        
        # 根据等级调整边界颜色
        level_colors ={
    0: (0, 255, 0),         # 绿色 - 无带状组织
    0.5: (0, 255, 85),      # 黄绿色 - 极其轻微
    1.0: (0, 255, 170),     # 浅绿色 - 很轻微
    1.5: (0, 255, 255),     # 黄色 - 轻微
    2.0: (0, 170, 255),     # 橙黄色 - 轻度
    2.5: (0, 85, 255),      # 橙色 - 中度
    3.0: (0, 0, 255),       # 红色 - 中重度
    3.5: (42, 0, 213),      # 深红色 - 重度
    4.0: (85, 0, 170),      # 紫红色 - 严重
    4.5: (128, 0, 128),     # 紫色 - 很严重
    5.0: (170, 0, 85),      # 深紫色 - 非常严重
    5.5: (213, 0, 42),      # 暗红色 - 极其严重
    6.0: (255, 0, 0)        # 纯红色 - 极度危险
}
        
        cv2.rectangle(overlay_img, (x, y), (x+w, y+h), level_colors[level], thickness)
        
        # 在窗口中心标注等级
        center_x = x + w // 2
        center_y = y + h // 2
        cv2.putText(overlay_img, str(level), (center_x-5, center_y+5), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, level_colors[level], 1)

def create_legend(overlay_img, level_map):
    """
    在图像上创建图例
    """
    h, w = overlay_img.shape[:2]
    
    # 创建图例背景
    legend_bg = np.zeros((150, 200, 3), dtype=np.uint8)
    
    # 等级颜色和说明
    level_info = [
        (0, "无带状组织", (0, 255, 0)),
        (1, "轻微", (255, 255, 0)),
        (2, "中等", (0, 165, 255)),
        (3, "严重", (0, 0, 255))
    ]
    
    # 绘制图例
    for i, (level, text, color) in enumerate(level_info):
        y_pos = 30 + i * 30
        cv2.rectangle(legend_bg, (10, y_pos-10), (30, y_pos+5), color, -1)
        cv2.putText(legend_bg, text, (40, y_pos), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
    
    # 计算图例在图像上的位置
    legend_x = w - 210
    legend_y = 10
    
    # 将图例叠加到图像上
    overlay_img[legend_y:legend_y+150, legend_x:legend_x+200] = cv2.addWeighted(
        overlay_img[legend_y:legend_y+150, legend_x:legend_x+200], 0.3, 
        legend_bg, 0.7, 0
    )
    
    return overlay_img

def visualize_complete_analysis(original_img, filtered_img, window_results, level_map, final_level, level_stats):
    """
    完整的可视化分析
    
    参数:
        original_img: 原始图像
        filtered_img: 过滤后的二值图像
        window_results: 窗口分析结果
        level_map: 等级图
        final_level: 最终等级
        level_stats: 等级统计信息
    """
    # 1. 将等级图叠加到原图上
    overlay_img, colored_level_map = overlay_level_map_on_original(original_img, level_map, alpha=0.6)
    
    # 2. 绘制窗口边界
    draw_window_boundaries(overlay_img, window_results, thickness=1)
    
    # 3. 添加图例
    # overlay_img = create_legend(overlay_img, level_map)
    
    # 4. 添加统计信息和最终等级
    # level_texts = ['无带状组织', '轻微带状组织', '中等带状组织', '严重带状组织']
    cv2.putText(overlay_img, f'finallevel: {final_level}', 
               (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.7, (255, 255, 255), 2)
    
    # 添加等级分布信息
    # dist_text = f"level_distribution: 0:{level_stats['level_distribution'].get(0,0)} "
    # dist_text += f"0.5:{level_stats['level_distribution'].get(0.5,0)} "
    # dist_text += f"1.0:{level_stats['level_distribution'].get(1.0,0)} "
    # dist_text += f"1.5:{level_stats['level_distribution'].get(1.5,0)}"
    # dist_text += f"2.0:{level_stats['level_distribution'].get(2.0,0)} "
    # dist_text += f"2.5:{level_stats['level_distribution'].get(2.5,0)} "
    # dist_text += f"3.0:{level_stats['level_distribution'].get(3.0,0)}"
    # dist_text += f"3.5:{level_stats['level_distribution'].get(3.5,0)}"
    # dist_text += f"4.0:{level_stats['level_distribution'].get(4.0,0)} "
    # dist_text += f"4.5:{level_stats['level_distribution'].get(4.5,0)} "
    # dist_text += f"5.0:{level_stats['level_distribution'].get(5.0,0)}"
    
    # cv2.putText(overlay_img, dist_text, 
    #            (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
    
    # 创建对比显示图像
    if len(filtered_img.shape) == 2:
        filtered_img_display = cv2.cvtColor(filtered_img, cv2.COLOR_GRAY2BGR)
    else:
        filtered_img_display = filtered_img.copy()
    # 绘制轮廓
    res_mask = original_img.copy()
    ctrs, _ = cv2.findContours(filtered_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    colors = [(0, 255, 0), (0, 0, 255), (255, 0, 0), (0, 255, 255)]
    if len(ctrs)>0:
        cv2.drawContours(
                    res_mask, ctrs, -1, colors[0], 5
                )
    cv2.putText(filtered_img_display, f'finallevel: {final_level}', 
               (50, 80), cv2.FONT_HERSHEY_SIMPLEX, 2.7, (12, 13, 253), 6)
    cv2.putText(res_mask, f'finallevel: {final_level}', 
               (50, 80), cv2.FONT_HERSHEY_SIMPLEX, 2.7, (12, 13, 253), 6)
    # 水平拼接原图、二值图和叠加图
    comparison = np.hstack([original_img, filtered_img_display,res_mask, overlay_img])
    
    return overlay_img, comparison,res_mask
# 使用示例
if __name__ == "__main__":
    # 示例参数（根据实际检测数据填写）
    sample_params = {
        # 'L1_total': 750,    # 铁素体带总长度
        # 'L2_total': 600,    # 第二相带总长度
        'n1': 1,            # ≥800μm的带数量
        'n2': 3,            # <800μm的带数量
        'band_lengths': [850, 500, 300, 450],  # 所有铁素体带长度
        'n1_band_widths': [15, 20, 18]         # 贯穿带宽度（仅n1类）
    }
    
    # 执行评级
    rating = rate_banded_structure(sample_params)
    print(f"带状组织评级结果: {rating}级")